package sjtu.ist.warehouse4ws.tool;

import java.io.File;
import java.io.InputStream;
import java.io.StringBufferInputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Map.Entry;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;

public class GenerateWsdl {
	public HashMap<String,String> functionHM;
	public HashMap<String,String> domainHM;
	

	
	public HashMap<String, String> getFunctionHM() {
		return functionHM;
	}


	public void setFunctionHM(HashMap<String, String> functionHM) {
		this.functionHM = functionHM;
	}


	public HashMap<String, String> getDomainHM() {
		return domainHM;
	}


	public void setDomainHM(HashMap<String, String> domainHM) {
		this.domainHM = domainHM;
	}


	public String writeWsdl(String outfile,String key) throws Exception{
		
		ArrayList<String> list=parserXml(key);
		ArrayList<String> list1=combineList(key);
		String functionName=readModelName(new StringBufferInputStream(functionHM.get(key)));
		String operationPattern=getOperationPattern(new StringBufferInputStream(functionHM.get(key)));
		HashMap mymap=readOperationParam(new StringBufferInputStream(functionHM.get(key)));
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		DocumentBuilder db = dbf.newDocumentBuilder();
		Document doc=db.newDocument();
		Element description=doc.createElement("description");
		description.setAttribute("xmlns", "http://www.sjtu.isl/ns/wsdl");
		description.setAttribute("xmlns:wsoap", "http://www.sjtu.isl/2003/05/soap");
		description.setAttribute("targetNamespace", "http://www.fuctions/wsdl/"+functionName);
		description.setAttribute("xmlns:tns", "http://www.fuctions/wsdl/"+functionName);
		doc.appendChild(description);
		
		Element documentation=doc.createElement("documentation");
		description.appendChild(documentation);
		Text content=doc.createTextNode("this document describes the "+functionName+" Web service.");
		documentation.appendChild(content);
		
		
		
		Element types=doc.createElement("types");
		description.appendChild(types);
		Element schema=doc.createElement("xml:schema");
		schema.setAttribute("xmlns:xs", "http://www.sjtu.isl/"+functionName+"/XMLSchema");
		types.appendChild(schema);
		if(list!=null){
		for(int i=0;i<list.size();i=i+2){                              //create function param
			Element rootele=doc.createElement("xs:element");
			rootele.setAttribute("name", list.get(i));
			rootele.setAttribute("type", list.get(i+1));
			schema.appendChild(rootele);
			}
		}
		
		if(list1!=null){                                    //if the root xml have no complexType, do nothing
			for(int k=0;k<list1.size();k++){
				String str=readModelName(new StringBufferInputStream(domainHM.get(list1.get(k))));
				Element complexType=doc.createElement("xs:complexType");
				complexType.setAttribute("name",str);
				schema.appendChild(complexType);
				Element sequence=doc.createElement("xs:sequence");
				complexType.appendChild(sequence);
				
				Element id=doc.createElement("xs:element");
				id.setAttribute("name", "id");
				id.setAttribute("type", "string");
				sequence.appendChild(id);
				
				
				ArrayList<String> list3=parserDomainmodelXml(new StringBufferInputStream(domainHM.get(list1.get(k))));
				for(int m=0;m<list3.size();m=m+2){
					Element ele=doc.createElement("xs:element");
					ele.setAttribute("name",list3.get(m));
					ele.setAttribute("type", list3.get(m+1));
					
					sequence.appendChild(ele);
				}
			}
			}
		
		
		Element inface=doc.createElement("interface");
		inface.setAttribute("name", functionName+"Interface");
		description.appendChild(inface);
		Element operation=doc.createElement("operation");
		operation.setAttribute("name", functionName);
		operation.setAttribute("pattern", "http://www.w3.org/ns/wsdl/"+operationPattern);   //here can be decided by model
		inface.appendChild(operation);
		if(operationPattern!=null){
		if(operationPattern.equals("in-out")){
			Iterator it=mymap.entrySet().iterator();
			  while(it.hasNext())      
			  {
				  Map.Entry entity = (Entry) it.next();
				  if(entity.getValue().equals("input")){
					  Element input=doc.createElement("input");
					  input.setAttribute("messageLabel", "In");
					  input.setAttribute("element", "ghns:"+entity.getKey());
					  operation.appendChild(input);
				  }

			  }
			  Iterator it1=mymap.entrySet().iterator();
			  while(it1.hasNext()){
				  Map.Entry entity = (Entry) it1.next();
				  if(entity.getValue().equals("output")){
						Element output=doc.createElement("output");
						output.setAttribute("messageLabel", "Out");
						output.setAttribute("element", "ghns:"+entity.getKey());
						operation.appendChild(output);
					  }
			  }
			
		}
		if(operationPattern.equals("in-only")){
			for(int i=0;i<list.size();i=i+2){
			Element input=doc.createElement("input");
			input.setAttribute("messageLabel", "In");
			input.setAttribute("element", "ghns:"+list.get(i));
			operation.appendChild(input);
			}
			
			
		}
		if(operationPattern.equals("out-only")){
			for(int i=0;i<list.size();i=i+2){
				Element output=doc.createElement("output");
				output.setAttribute("messageLabel", "Out");
				output.setAttribute("element", "ghns:"+list.get(i));
				operation.appendChild(output);
		}
		
		}
		}
		Element binding=doc.createElement("binding");
		binding.setAttribute("name", functionName+"SOAPBinding");
		binding.setAttribute("interface",functionName+"Interface");
		binding.setAttribute("type", "http://www.sjtu.isl/ns/wsdl/soap");
		description.appendChild(binding);
		Element opt=doc.createElement("operation");
		opt.setAttribute("ref", "tns:"+functionName);
		
		binding.appendChild(opt);
		
		Element service=doc.createElement("service");
		service.setAttribute("name", functionName+"Service");
		service.setAttribute("interface", functionName+"Interface");
		description.appendChild(service);
		Element endpoint=doc.createElement("endpoint");
		endpoint.setAttribute("name", functionName+"Endpoint");
		endpoint.setAttribute("binding", functionName+"SOAPBinding");
		endpoint.setAttribute("address", "http://www.sjtu.isl/"+functionName);
		service.appendChild(endpoint);
		System.out.print("wsdl finish !");
		
		
		return domDocToFile(doc,outfile,"utf-8");
	}
	
	
	public  String domDocToFile(Document doc, String fileName,String encoding) throws TransformerException{  //create the XML file
		TransformerFactory tfactory=TransformerFactory.newInstance();
		Transformer transformer=tfactory.newTransformer();
		Properties properties=transformer.getOutputProperties();
		properties.setProperty(OutputKeys.ENCODING, encoding);
		properties.setProperty(OutputKeys.METHOD, "xml");
		properties.setProperty(OutputKeys.INDENT, "yes");            //control line
		transformer.setOutputProperties(properties);
		
		DOMSource source=new DOMSource(doc);
		File file=new File(fileName);
		StreamResult result=new StreamResult(file);
		
		transformer.transform(source, result);

		return file.getAbsolutePath();
	}
	public ArrayList<String> parserXml(String key) throws Exception{     //get the model attribute
		ArrayList<String> paramName=new ArrayList<String>();
		Document doc=initDoc(new StringBufferInputStream(functionHM.get(key)));

		NodeList nodelist= doc.getElementsByTagName("parameter");  
		for(int i=0;i<nodelist.getLength();i++){
			Element dma=(Element)nodelist.item(i);
			NodeList str=dma.getElementsByTagName("enName");
			Element paramElement=(Element) str.item(0);
			Text paramNode=(Text) paramElement.getFirstChild();
			String str1=paramNode.getNodeValue();
			paramName.add(str1);
			
			
			NodeList str2=dma.getElementsByTagName("paramTypeName");
			Element e1=(Element) str2.item(0);
			Text t1=(Text) e1.getFirstChild();
			String str3=t1.getNodeValue();
			paramName.add(str3);

		}
		
		return paramName;
		
	}
	public ArrayList<String> parserDomainmodelXml(InputStream is) throws Exception{     //get the model attribute
		ArrayList<String> paramName=new ArrayList<String>();
		Document doc=initDoc(is);

		NodeList nodelist= doc.getElementsByTagName("domain_model_attribute");  
		for(int i=0;i<nodelist.getLength();i++){
			Element dma=(Element)nodelist.item(i);
			NodeList str=dma.getElementsByTagName("enName");
			Element paramElement=(Element) str.item(0);
			Text paramNode=(Text) paramElement.getFirstChild();
			String str1=paramNode.getNodeValue();
			paramName.add(str1);
			
			
			NodeList str2=dma.getElementsByTagName("enType");
			Element e1=(Element) str2.item(0);
			Text t1=(Text) e1.getFirstChild();
			String str3=t1.getNodeValue();
			paramName.add(str3);

		}
		
		return paramName;
		
	}
	public ArrayList<String> readPath(InputStream is) throws Exception{                   //get children path
		 ArrayList<String> pathList=new ArrayList();
		
		Document doc=initDoc(is);
		NodeList nodelist = doc.getElementsByTagName("parameter");  // get the child path from current xml
		for (int i = 0; i < nodelist.getLength(); i++) {
			Element dma = (Element) nodelist.item(i);
			
			NodeList str = dma.getElementsByTagName("paramTypeId");
			Element paramElement = (Element) str.item(0);
			if(paramElement!=null){
			Text paramNode = (Text) paramElement.getFirstChild();
			if(paramNode!=null){
			String str1 = paramNode.getNodeValue();
			if (str1.length() > 2) {

				str1=str1+".xml";     //path should care
				pathList.add(str1);			

			}
			}
			}
		}

		return pathList;
}
	public ArrayList<String> readDomainmodelPath(String key) throws Exception{              
		 ArrayList<String> pathList=new ArrayList();
		Document doc=initDoc(new StringBufferInputStream(domainHM.get(key)));
		NodeList nodelist = doc.getElementsByTagName("domain_model_attribute");  
		for (int i = 0; i < nodelist.getLength(); i++) {
			Element dma = (Element) nodelist.item(i);
			NodeList str = dma.getElementsByTagName("typeId");
			Element paramElement = (Element) str.item(0);
			if(paramElement!=null){
			Text paramNode = (Text) paramElement.getFirstChild();
			if(paramNode!=null){
			String str1 = paramNode.getNodeValue();
			if (str1.length()>2) {

				str1=str1+".xml";     //path should care
				pathList.add(str1);
				readPath(new StringBufferInputStream(domainHM.get(str1)));                                            //DFS use recursion
			
			}
			}
			}
		}

		return pathList;
}
	public ArrayList<String> combineList(String key) throws Exception{     // all the path can be get
		
		
		ArrayList<String> list1=readPath(new StringBufferInputStream(functionHM.get(key)));  //parameter type id
		
		ArrayList<String> list2 =new ArrayList();
		ArrayList<String> list3=readPath(new StringBufferInputStream(functionHM.get(key)));
		for(int i=0;i<list1.size();i++){
			
			list2=readDomainmodelPath(list1.get(i));
			list3.addAll(list2);
			
		}	
		
		return list3;
		
	}
	public Document initDoc(InputStream is) throws Exception{
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		DocumentBuilder db = dbf.newDocumentBuilder();
		          
		Document doc = db.parse(is);
		return doc;
	}
	
	public String readModelName(InputStream is) throws  Exception  {               //get the complex name
		Document doc=initDoc(is);
	
		NodeList nodelist = doc.getElementsByTagName("enName");
		Element dma = (Element) nodelist.item(0);
		Text paramNode = (Text) dma.getFirstChild();
		String str1 = paramNode.getNodeValue();
	
		return str1;
	}
	
	public String getOperationPattern(InputStream is) throws Exception{
		String pattern=null;
		 ArrayList<String> pathList=new ArrayList();
		Document doc=initDoc(is);
		NodeList nodelist = doc.getElementsByTagName("parameter");  
		for (int i = 0; i < nodelist.getLength(); i++) {
			Element dma = (Element) nodelist.item(i);
			
			NodeList str = dma.getElementsByTagName("inOutType");
			Element paramElement = (Element) str.item(0);
			if(paramElement!=null){
			Text paramNode = (Text) paramElement.getFirstChild();
			if(paramNode!=null){
			String str1 = paramNode.getNodeValue();
			pathList.add(str1);			
			}
			}
		}
	//	System.out.print(pathList.size());
		HashSet h = new HashSet(pathList);         //clean the repeat 
		pathList.clear();      
		pathList.addAll(h);   
	//	System.out.print(pathList.size());
		if (pathList.size()==2) {
			pattern="in-out";
		
		} else if (pathList.size()==1) {
			if (pathList.get(0).equals("input")) {
				pattern="in-only";
			
			}
			if (pathList.get(0).equals("output")) {
				pattern="out-only";
				
			}

		} 
		System.out.print(pattern);
		return pattern;
	
		
	}
	
	public HashMap readOperationParam(InputStream is) throws Exception{
		HashMap mymap=new HashMap();
		Document doc=initDoc(is);

		NodeList nodelist= doc.getElementsByTagName("parameter");  
		for(int i=0;i<nodelist.getLength();i++){
			Element dma=(Element)nodelist.item(i);
			NodeList str=dma.getElementsByTagName("enName");
			Element paramElement=(Element) str.item(0);
			Text paramNode=(Text) paramElement.getFirstChild();
			String str1=paramNode.getNodeValue();

			NodeList str2=dma.getElementsByTagName("inOutType");
			Element e1=(Element) str2.item(0);
			Text t1=(Text) e1.getFirstChild();
			String str3=t1.getNodeValue();
			mymap.put(str1, str3);

		}
		
		
//		  Iterator it = mymap.keySet().iterator();
//
//		  while(it.hasNext())      //第一种迭代方式取键值
//		  {
//		   Object key = it.next();
//		   System.out.println(key+" : "+mymap.get(key)); //根据键来取对应的值
//		  }
		
//		Iterator it=mymap.entrySet().iterator();
//		  while(it.hasNext())      
//		  {
//			  Map.Entry value = (Entry) it.next();
//		   System.out.println(value.getKey()); 
//		   System.out.println(value.getValue()); 
//		  }

		return mymap;
	}

	
	
//	public static void main(String[] args) throws Exception {
//		GenerateWsdl gws=new GenerateWsdl();
//		String outfile="./src/sjtu/ist/warehouse4ws/tool/MyWsdl.xml";
//		String path="E:/Users/chenqidou/Desktop/wsdl/5a3981b7-ca60-4b66-bbab-4343a6615f11.xml";
//		String path1="E:/Users/chenqidou/Desktop/wsdl/198fa1c7-67b3-4686-9e06-5b4b88d47b2f.xml";
//	//	gws.writeWsdl(outfile, path);
//	//	gws.readOperationParam(path);
//	//	String str=gws.showOperationPattern(path);
//	//	System.out.print(str);
//		gws.getOperationPattern(path);
//
//
//	
//	}
}
